#ifndef _DICOM_SYNC_MANAGER_
#define _DICOM_SYNC_MANAGER_

#include <deque>
#include <gwbase/base/condvar.h>
#include <gwbase/base/timestamp.h>
#include <gwbase/db/sqlite3_wrapper.h>
#include <gwbase/base/thread.h>
#include <surbasic/TreeEntity.h>
#include "dicom/dicom_conf.h"
#include "dicom/file_object.h"
#include "dicom/dmv_db.h"
#include "dicom/upload/hash_cache.h"

namespace dicom {

struct Task {
  std::string id;
  std::string user;
  bool running;
  Timestamp next_retry_time;
};

inline bool TaskCompareRetryTime(const Task* t1, const Task* t2) {
  assert(t1 != NULL && t2 != NULL);
  return t1->next_retry_time < t2->next_retry_time;
}

typedef std::deque<Task> TaskList;

class SyncManager;
class SyncPerUserRetryWorker {
 public:
  SyncPerUserRetryWorker(SyncManager* mgr, const std::string& user)
      : mgr_(mgr), user_(user) {}

  void ScheduleRetry(Task& t, int delay);

  void Start();

 private:
  void PollRetryTasks();

  SyncManager* mgr_;
  Mutex mu_;
  std::vector<Task*> waiting_retry_tasks_;
  CondVar not_empty_;
  ThreadPtr worker_;
  std::string user_;
};

class SyncManager {
 public:
  SyncManager(const DicomConf* conf);
  ~SyncManager() {}

  void StartWorkers(int n);
  void AddNewTask(const std::string& user, const std::string& id,
                  const std::string& pid);

  void DeleteTask(const std::string& id);

  void AddNewUser(const std::string& user);

  void LoadPendingTasks();

 private:
  friend class SyncPerUserRetryWorker;

  void RunWorkerLoop();
  void GetNextTask(Task* t);
  void ReaddTask(Task* t);
  void ScheduleRetry(Task& t, int delay);

  bool UploadFileToCloud(int64_t token, FileObject& fo,
                         const std::string& parent_cloud_id,
                         const std::string& enc_file_path,
                         SDBasic::tree::TreeEntity* node);

  bool UploadFileToCloudV2(int64_t token, DmvDBPtr& dmv, FileObject& fo,
                           const std::string& enc_file_path,
                           SDBasic::tree::TreeEntity* node);

  bool UploadBigFile(uint64_t surdoc_key, FileObject& fo,
                     const std::string& parent_cloud_id,
                     const std::string& enc_file_path, const HashEntry* hash,
                     SDBasic::tree::TreeEntity* node);

  db::DbPtr db_;
  TaskList tasks_;
  Mutex mu_;
  CondVar not_empty_;

  typedef std::map<std::string, SyncPerUserRetryWorker*> RetryWorkerMap;
  RetryWorkerMap retry_workers_;
};

SyncManager* NewSyncManager(const DicomConf* conf);
}

#endif  // _DICOM_SYNC_MANAGER_
